import { director, find, log } from 'cc';
import { App } from '../App';
import { EventConst } from '../common/Common';
import Config from '../common/Config';
import CommConst from '../const/CommConst';
import GameConst from '../const/GameConst';
import { PD } from '../player/PlayerData';
import { ByteStream } from '../tools/ByteStream';
import { LoadMg } from '../tools/LoadMg';
import { showTipBox } from '../tools/PopuUp';
import UtilTool from '../tools/UtilTool';

// import JSEncrypt from 'jsencrypt'
import CryptoAES from '../tools/CryptoAES'

export class WebSocketTool {
  private ws!: WebSocket;
  private url!: string;
  private isOpen = false;
  private requestQueue = []; //连接队列
  private isReconnecting = false;
  private isHeartbeating = false;
  private heartbeatingInterval = 3000;
  private heartbeatingTimer = null;
  private reconnectCount = 10  // 断线重连次数
  private onMessageListeners: ((data: any) => void)[] = [];

  static instance: WebSocketTool;
  static openReslove: (value: unknown) => void;
  static openReject: (reason: unknown) => void;

  constructor(url: string) {
    this.url = url;
    this.connect();
  }

  public static getInstance(url: string, reslove: (value: unknown) => void, reject: (reason?: any) => void): WebSocketTool {
    UtilTool.printLog('连接url', url);

    if (!WebSocketTool.instance) {
      WebSocketTool.instance = new WebSocketTool(url);
      WebSocketTool.openReslove = reslove;
      WebSocketTool.openReject = reject;
    }
    return WebSocketTool.instance;
  }
  connect() {
    UtilTool.printLog("connect >>> 请求连接socket")
    this.clearWs()

    this.ws = new WebSocket(this.url);
    // this.ws.binaryType = "arraybuffer";//"blob"
    this.ws.onopen = this.handleOpen.bind(this);
    this.ws.onclose = this.handleClose.bind(this);
    this.ws.onmessage = this.handleMessage.bind(this);
    this.ws.onerror = this.handleError.bind(this);
  }

  clearWs() {
    if(this.ws) {
      this.ws.onopen = null
      this.ws.onclose = null
      this.ws.onmessage = null
      this.ws.onerror = null
      this.ws = null
      
    }
  }

  // 主动退出
  activeClose() {
    if(this.ws) {
      this.ws.close()
    }
    this.clearWs()
    this.stopHeartbeating()
    WebSocketTool.instance = null
  }

  reconnect() {

    if (!this.isOpen && !this.isReconnecting) {
      if (this.reconnectCount<=-1) {
        UtilTool.printLog("reconnectCount重连次数", this.reconnectCount)
        return
      }

      if (this.reconnectCount<=0) {
        this.reconnectCount = -1
        showTipBox({
          content:App.language.getLanguageLab(35),
          func:()=>{
            LoadMg.showLoad({type:1})
            App.PoolManager.clear()
            delete WebSocketTool.instance
            WebSocketTool.instance = null

            App.Scene.go("login");
          }
        })

        return
      }

      UtilTool.printLog("请求网络重连",this.reconnectCount)
      this.isReconnecting = true;
      this.reconnectCount = this.reconnectCount - 1
      setTimeout(() => {
        this.connect();
        // this.isReconnecting = false;
      }, 3000);
    }
  }

  /**
   * 开始心跳
   */
  startHeartbeating() {
    if (!this.isHeartbeating) {
      this.isHeartbeating = true;
      this.heartbeatingTimer = setInterval(() => {
        if (this.isOpen) {
          // this.send('ping');

          let playerUserId = PD.playerInfo.userId;
          this.send({msgCode:CommConst.Req_HEARTBEAT,"userId":playerUserId,"noLoad":true})
        } else {
          this.stopHeartbeating();
          this.reconnect();
        }
      }, this.heartbeatingInterval);
    }
  }

  stopHeartbeating() {
    this.isHeartbeating = false;
    if (this.heartbeatingTimer) {
      clearInterval(this.heartbeatingTimer);
      this.heartbeatingTimer = null;
    }
  }

  /**
   * 发送数据
   */
  send(data: any): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.isOpen) {
        if (!data.noLoad) {
          LoadMg.showLoad()
        }

        UtilTool.printLog(data, data.msgCode, '发送数据');

        let code = data.msgCode;
        const message = { ...data }

        // 加密发送
        let enData = CryptoAES.encrypt(JSON.stringify(message))
        this.ws.send(enData);
        
        const listener = (responseData: any) => {
          if (responseData.msgCode === code) {
            if (!data.noLoad) {
              LoadMg.removeLoad()
            }

            resolve(responseData);
            this.removeMessageListener(listener);
          }
        };
        this.addMessageListener(listener);

        /* UtilTool.printLog(data,"send data:")
        this.ws.send(data); */
      } else {
        reject('WebSocket is not open');
      }
    });
  }


  addMessageListener(listener: (data: any) => void) {
    this.onMessageListeners.push(listener);
  }

  removeMessageListener(listener: (data: any) => void) {
    const index = this.onMessageListeners.indexOf(listener);
    if (index !== -1) {
      this.onMessageListeners.splice(index, 1);
    }
  }

  private handleOpen() {
    UtilTool.printLog('WebSocket connected');
    this.isOpen = true;
    this.startHeartbeating();
    WebSocketTool.openReslove(true);
    this.reconnectCount = 10
    if (this.isReconnecting) {
      this.isReconnecting = false;
      let playerUserId = PD.playerInfo.userId;

      // setTimeout(()=> {
        this.send({"msgCode":CommConst.Req_RECONNECT, "token":PD.user.token, "userId":playerUserId, "gameStatus":App.WSGameStatus})
        .then(res =>{
          UtilTool.printLog(res,'重连状态');
          App.Model.gameManage.reconnectWatch()
        })
        
      // },2000)

    }
  }

  private handleClose() {
    UtilTool.printLog('WebSocket disconnected');
    this.isOpen = false;
    this.isReconnecting = false;
    this.ws.close();//先尝试关闭再重连
    // this.stopHeartbeating();
    this.reconnect();
  }

  /**
   * 接收数据
   */
  private handleMessage(event: MessageEvent) {
    let enData = CryptoAES.decrypt(event.data)
    const data = JSON.parse(enData)
    
    UtilTool.printLog(data, data.msgCode, '接收数据');

    // 主动推送更新用户信息
    if (data.msgCode == GameConst.Resp_UPDATE_INFO) {
      UtilTool.printLog('更新用户信息的data',data);
      App.Event.emit(EventConst.UPDATA_USERINFO, data);
    }

    // 主动推送
    if (data.msgCode == GameConst.Resp_GAME_WIN) {
      UtilTool.printLog('502通知来了');
      App.Event.emit(EventConst.UPLOAD_DATA, data)
    }

    // 幸运球状态发生变化
    if (data.msgCode == CommConst.Resp_MODDE1_STATUS) {
      UtilTool.printLog('幸运球状态发生变化');
      App.Event.emit(EventConst.UPDATE_MODE1_STATUS_CHANGE, data)
    }
    
    // 主动推送断线重连游戏数据
    /* if (data.msgCode == CommConst.Req_ENTERGAME && data.isReconnect==1) {
      App.Event.emit(EventConst.UPDATE_GAMEDATA_RECONNET, data)
    } */

    // 主动推送更新提现记录信息
    if (data.msgCode == GameConst.Resp_UPDATE_WITHDRAW_JL) {
      App.Event.emit(EventConst.UPDATE_WITHDRAW_JL, data)
    }

    // 游戏有关消息通知
    App.Model.gameManage.respGameMsg(data)

    // 游戏消息通知(只在游戏内走这里)
    if (App.Model.gameController) {
      App.Model.gameController.respGameMsg(data)
    }

    //   UtilTool.printLog('WebSocket message received', data);
    this.onMessageListeners.forEach(listener => listener(data));

    /* UtilTool.printLog(event.data,"rev data:")
    let byteStream:ByteStream = new ByteStream(event.data);
    var v1 = byteStream.readByte()
    var v2 = byteStream.readUsort()
    byteStream.Offset += 1
    var v3 = byteStream.readSort()
    byteStream.Offset += 2
    var v4 = byteStream.readUint()
    var v5 = byteStream.readInt()
    var v6 = byteStream.readFloat() */

    // var len = byteStream.readUsort()
    // var mid = byteStream.readUsort()
    // byteStream.Offset = CommConst.HEAD_LEN;
    // let buf = new Uint8Array( byteStream.readByteArray(len-CommConst.HEAD_LEN));
    // App.Event.emit(EventConst.SOCKET_ONMESSAGE,{mid:mid, buf:buf, len:len})
  }

  private handleError(event: Event) {
    // console.error('WebSocket error', event);
    UtilTool.printLog('WebSocket error', event);
    this.isOpen = false;
    this.isReconnecting = false;
    this.ws.close();
    // this.reconnect();
  }
}

const wsTool = {
  init: (url) => {
    return new Promise((reslove, reject) => {
      WebSocketTool.getInstance(Config.WSPROTOCOI + url, reslove, reject);
    })
  },
  send: (data) => {
    return WebSocketTool.instance.send(data);
  },
  connect: () => {
    return WebSocketTool.instance.connect();
  }
}
export default wsTool;
